#include <dim-sum/errno.h>
#include <dim-sum/init.h>
#include <dim-sum/simple_console.h>
#include <dim-sum/console.h>
#include <dim-sum/printk.h>
#include <dim-sum/devtree.h>
#include <dim-sum/platform.h>
#include <asm/early_map.h>
#include <asm/io.h>
#if defined(CONFIG_USE_FDT)
#include <asm/devtree.h>
#endif

/**
 * simple_console_id 变量声明
 */
#ifdef CONFIG_SERIAL_PL011
extern const struct simple_console_id simple_console_pl011;
#endif
#ifdef CONFIG_SERIAL_NS16550A
extern const struct simple_console_id simple_console_ns16550a;
#endif

#if defined (CONFIG_SERIAL_SBI)
extern const struct simple_console_id simple_console_sbi;
#endif
#if defined CONFIG_SERIAL_x86_64
extern const struct simple_console_id simple_console_x86_64;
#endif

static struct console simple_console = {
	.name =		"uart",
	.flags =	CONSOLE_PRINTK | CONSOLE_SIMPLE,
	.index =	-1,
};

static struct simple_console_device simple_console_dev = {
	.con = &simple_console,
};

/**
 * 注册与使用存在时序，不需要锁保护
 */
struct double_list console_match_table = 
	LIST_HEAD_INITIALIZER(console_match_table);

void __iomem * __init simple_console_map(phys_addr_t paddr, size_t size)
{
	void __iomem *base;
#ifndef CONFIG_LOONGARCH64
	early_mapping(EARLY_MAP_EARLYCON, paddr & PAGE_MASK, EARLY_MAP_IO);
	base = (void __iomem *)early_map_to_virt(EARLY_MAP_EARLYCON);
	base += paddr & ~PAGE_MASK;
#else
	base = ioremap(paddr, size);
#endif
	return base;
}

extern struct device_driver dw_uart_driver;
extern struct device_driver ns16550a_uart_driver;
int uart_init(void)
{
#ifdef CONFIG_SERIAL_NS16550A
	platform_driver_register_new(&ns16550a_uart_driver);
#endif
	return 0;
}

__maybe_unused static int __init register_simple_console(void *priv, const struct simple_console_id *match)
{
	int err;

	simple_console_dev.con->data = &simple_console_dev;
	err = match->init(&simple_console_dev, priv);
	if (err < 0)
		return err;
	if (!simple_console_dev.con->write)
		return -ENODEV;

	register_console(simple_console_dev.con);
	return 0;
}

static int __init setup_simple_console(const struct simple_console_id *match,
		phys_addr_t membase, void *opt)
{
	int err;
	/**
	 * simple_console初始化
	 */
	simple_console_dev.con->data = &simple_console_dev;
	simple_console_dev.membase = simple_console_map(membase, PAGE_SIZE);
	err = match->init(&simple_console_dev, opt);
	if (err < 0)
		return err;
	if (!simple_console_dev.con->write)
		return -ENODEV;
	/**
	 * console系统注册
	 */
	register_console(simple_console_dev.con);
	pr_info("注册简单控制台:%s\n", match->name);
	return 0;
}

static int compatible_check(const char *complist, int complen, const char *str)
{
	int len = strlen(str);
	const char *p;

	while (complen >= len) {
		if (memcmp(str, complist, len+1) == 0)
			return 1;
		p = memchr(complist, '\0', complen);
		if (!p)
			return 0; /* malformed strlist.. */
		complen -= (p-complist) + 1;
		complist = p + 1;
	}
	return 0;
}

__maybe_unused static int __init add_simple_console(const struct simple_console_id *console)
{
	list_insert_behind((struct double_list*)(&console->list), &console_match_table);
	return 0;
}

int __init setup_simple_console_by_name(const char *compatible, uint32_t len, phys_addr_t membase, void *opt)
{
	struct double_list *entry;
	struct simple_console_id *matche;
	/**
	 * 正确匹配simple console
	 */
	list_for_each(entry, &console_match_table) {
		matche = list_container(entry, struct simple_console_id, list);
		if (compatible_check(compatible, len, matche->name)) {
			setup_simple_console(matche, membase, opt);
			return 0;
		}
	}
	return -ENODEV;
}

static int __init add_all_simple_console(void) {
#ifdef CONFIG_SERIAL_PL011
	add_simple_console(&simple_console_pl011);
#endif
#ifdef CONFIG_SERIAL_NS16550A
	add_simple_console(&simple_console_ns16550a);
#endif

#if defined (CONFIG_SERIAL_SBI)
	add_simple_console(&simple_console_sbi);
#endif
	return 0;
}

int __init init_simple_console(char *buf)
{
	add_all_simple_console();

	if (!strcmp(buf, "riscv,sbi")) {
		/**
		 * 由于 riscv sbi 存在 串口功能，
		 * 不需要读取设备树即可直接使用
		 */
		setup_simple_console_by_name(buf, strlen(buf), (phys_addr_t)NULL, NULL);
	} else if (!strcmp(buf, "mips,ns16550a")) {
		/**
		 * 当前该设备未支持设备树解析，先临时这么写，后续需要改为设备树实现
		 */
#ifdef CONFIG_SERIAL_NS16550A
		register_simple_console(buf, &simple_console_ns16550a); 
#endif
	} else {
#if defined(CONFIG_USE_FDT)
		/**
		* 解析得到stdout设备树节点信息
		*/
		early_init_dt_match_stdout();
#endif
#ifdef CONFIG_SERIAL_PL011
	setup_simple_console_by_name(buf, strlen(buf), 0x9000000, NULL);
	//register_simple_console(buf, &simple_console_pl011); 
#endif
#if defined(CONFIG_ARCH_D2000)
		setup_simple_console_by_name(buf, strlen(buf), 0x28001000, NULL);
#endif
#ifdef CONFIG_SERIAL_x86_64
		register_simple_console(buf, &simple_console_x86_64);
#endif
	}

	pr_info("简单控制台已经准备就绪。\n");

	return 0;
}
